package auth

import (
	"context"
	"crypto/rand"
	"crypto/rsa"
	"encoding/base64"
	"manager/collector/logger"
	"manager/config"
	"manager/errors"
	"manager/lib/token"
	"manager/lib/token/jwt"
	"manager/utils"
	"time"
)

const (
	AuthUserKey = "auth_user_key"
	AuthOsKey   = "auth_os_key"
)

type TokenDataKey struct{}

var auth token.Provider

type Options struct {
	UserID int64 `json:"user_id"`
}

func Init() func() {
	auth = jwt.NewTokenProvider(token.WithPrivateKey(config.Conf.JwtToken.PrivateKey), token.WithPublicKey(config.Conf.JwtToken.PublicKey))
	return func() {}
}

func Generate(options *Options, expire time.Duration) (*token.Token, error) {
	user := &token.UserClaims{
		UserID: options.UserID,
	}

	return auth.Generate(user, token.WithExpiry(expire))
}

func Refresh(tk string) (*token.Token, error) {
	return auth.Refresh(tk)
}

func Verify(tk string) error {
	_, err := auth.Parse(tk)
	if err != nil {
		return err
	}
	return nil
}

func Extract(tk string) (*Options, error) {
	var (
		err  error
		data *token.UserClaims
	)
	if data, err = auth.Parse(tk); err != nil {
		return nil, err
	}

	return &Options{UserID: data.UserID}, nil
}

func ExtractDataFromCtx(ctx context.Context) (*Options, error) {
	var (
		ok   bool
		opts = &Options{}
	)
	value := ctx.Value(TokenDataKey{})
	if opts, ok = value.(*Options); ok {
		return opts, nil
	}
	return nil, errors.New("context does not contain token data")
}

// ContextOs 获取平台
func ContextOs(ctx context.Context) (os string) {
	value := ctx.Value(AuthOsKey)
	var ok bool
	os, ok = value.(string)
	if !ok {
		if value != nil {
			logger.Errorf("ContextOs type err %T", value)
		}
		return
	}
	return
}

// ContextUserID context获取用户id
func ContextUserID(ctx context.Context) (int64, error) {
	var (
		userId int64
		err    error
	)

	value := ctx.Value(AuthUserKey)
	userId, ok := value.(int64)
	if !ok {
		if value != nil {
			logger.Errorf("context user id type err %T", value)
		}
		return userId, errors.New("token err")
	}
	return userId, err
}

// PasswordCheck 密码检查
func PasswordCheck(passwordIn, salt, passwordLocal string) (bool, error) {
	var (
		err error
	)

	passwordByte, err := base64.StdEncoding.DecodeString(passwordIn)
	if err != nil {
		logger.Errorf("password check base64 decodeString err %s", err.Error())
		return false, errors.Wrap(err, "密文不正确")
	}

	// rsa 解密
	pem, err := auth.ParseRSAPrivateKeyFromPEM()
	if err != nil {
		logger.Errorf("password check parse RSA privateKey from PEM err %s", err.Error())
		return false, err
	}
	//使用得到的私钥通过rsa进行数据解密
	bytes, err := rsa.DecryptPKCS1v15(rand.Reader, pem, passwordByte)
	if err != nil {
		logger.Errorf("password check decryptPKCS1v15 err %s", err.Error())
		return false, err
	}
	// sha256 加密 然后判断
	if utils.EncryptionSha256(string(bytes), salt) != passwordLocal {
		return false, nil
	}
	return true, nil
}
